<?php
/**    
  *
  * Copyright (c) 2009, Persistent Systems Limited
  *
  * Redistribution and use, with or without modification, are permitted 
  *  provided that the following  conditions are met:
  *   - Redistributions of source code must retain the above copyright notice, 
  *     this list of conditions and the following disclaimer.
  *   - Neither the name of Persistent Systems Limited nor the names of its contributors 
  *     may be used to endorse or promote products derived from this software 
  *     without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  
/**
  *
  * This file contains the definition of JSONParser class.
  */
  
class JSONParser
{
    protected $objectContext;
    protected $jsonStr;	
    protected static $METADATA = "__metadata";
    protected static $DEFERRED = "__deferred";
    
    public function JSONParser($jsonStr, $objectContext)
    {
        $this->objectContext = $objectContext;
        $this->jsonStr = Utility::decodeJSONSpecialChar($jsonStr);
    }
    
    public function EnumerateObjects()
    {
        $phpNativeJSON = json_decode($this->jsonStr, true);	
        if (is_array($phpNativeJSON))
        {
            if (array_key_exists("error", $phpNativeJSON) == TRUE)
            {
                throw new ADODotNETDataServicesException($phpNativeJSON);
            }
        }
        return $this->getEntityResult($phpNativeJSON);
    }
    
    /**
    * Wrapper function over getResult. This function
    * is implemented to return result as a collection.
    * @param array $phpNative Associative array.
    * @return Array $resultSet The entity set.
    */
    protected function getEntityResult($phpNative)
    {
        $resultSet = array();  
        $resourceBoxes = array();       
        if (is_array($phpNative)) {
            if (array_key_exists(self::$METADATA, $phpNative["d"])) 
            {
                $resultSet[] = self::getResult($phpNative["d"]);
            } 
            else 
            {
                foreach ($phpNative["d"] as $result) 
                {
                    $resultSet[] = self::getResult($result);
                }
            }
        }       
        return $resultSet;
    }

    /**
     * Function to parse the native php associative
     * array corrosponding to json response.
     * @param <array> $phpNative Associative array.
     * @return Array $object Entity Instance or Entity Set.
     */
    protected function getResult($phpNative, $sourceObject = null)
    {
        $type = $phpNative[self::$METADATA]["type"];
        $uri = $phpNative[self::$METADATA]["uri"];
        $entitySet = Utility::GetEntitySetFromUrl($uri);
        $entityType = $this->objectContext->GetEntityTypeNameFromSet($entitySet);
        $object = $this->objectContext->AddToObjectToResource($entityType, $uri);
        if($sourceObject != null)
        {
            $this->objectContext->AddToBindings($sourceObject, $entitySet, $object);
        }  
        $class = new ReflectionClass($entityType);
        $properties = $class->getProperties();
        $tmpKeys = array();
        foreach ($properties as $rawProperty) 
        {
            $propertyName = Utility::getPropertyName($rawProperty);
            $property = new ReflectionProperty($object, $propertyName);
           
            if ($property->isPublic()) 
            {
                if (array_key_exists($propertyName, $phpNative)) 
                {
                    if (is_array($phpNative[$propertyName])) 
                    {
                        $resultCollection = array();
                        $tempArray = $phpNative[$propertyName];
                        if (array_key_exists(self::$DEFERRED, $tempArray) == FALSE) 
                        {
                            foreach ($tempArray as $array) 
                            {
                                $resultCollection[] = self::getResult($array, $object);
                            }
                        } 
                        else 
                        {
                            $relUri = $phpNative[$propertyName][self::$DEFERRED]["uri"];
                            $index = Utility::reverseFind($relUri, '/');
                            $subRelUri = substr($relUri, 0, $index);
                            $index  = Utility::reverseFind($subRelUri, '/');
                            $subRelUri = substr($relUri, $index + 1);
                            $tmpKeys[$propertyName]= $subRelUri;
                        }
                        
                        $property->setValue($object, $resultCollection);
                    } 
                    else 
                    {
                        $value = $phpNative[$propertyName];
                        if (strpos($property->getDocComment(), 'Edm.DateTime') == TRUE) 
                        {
                            $value = Utility::jsonDateToPhpDate($value);
                        }
                        
                        $property->setValue($object, $value);
                    }
                }
            }
        }
        
        $method = $class->getMethod('setRelatedLinks');
        $method->invoke($object,$tmpKeys);
        return $object;
    }
    
    
    public static function PopulateObject($phpNative, $resource, &$uri)
    {
         if(!array_key_exists(self::$METADATA, $phpNative["d"]))
         {
                return;
         }
         $phpNative = $phpNative["d"];
         $type = $phpNative[self::$METADATA]["type"];
         $entityName = Utility::getEntityNameFromType($type);
         $uri = $phpNative[self::$METADATA]["uri"];
         $class = new ReflectionClass($entityName);
         $properties = $class->getProperties();	
         foreach ($properties as $rawProperty) 
         {
             $propertyName = Utility::getPropertyName($rawProperty);
             $property = new ReflectionProperty($resource, $propertyName);
             if ($property->isPublic()) 
             {
                $value = $phpNative[$propertyName];
                 if (strpos($property->getDocComment(), 'Edm.DateTime') == TRUE) 
                 {
                   $value = Utility::jsonDateToPhpDate($value);
                 }
                 $property->setValue($resource, $value);
             }
         }
    }
    
    public static function GetErrorDetails($errorValue, &$outerError, &$innerError)
    {
         if (is_string($errorValue))
         {
             $innerError = "";
             $outerError = $errorValue;
         }
         else
         {
            // Fetch details from json error structure
            $outerError = $innerError = "";
            
            if (array_key_exists("message", $errorValue["error"]) == TRUE)
            {
                  $outerError = $errorValue["error"]["message"]["value"];
                  
            }
            if (array_key_exists("innererror", $errorValue["error"]) == TRUE)
            {
                if (array_key_exists("internalexception", $errorValue["error"]["innererror"]) == TRUE)
                {
                    $innerError = $errorValue["error"]["innererror"]["internalexception"]["message"];
                }
            }
         }
    }
}

?>